<template>
	<!-- 陶老师AI智能分析 -->
	<view class="content">
		<view class="base-page-bg-img"><image class="img" src="/static/image/hszg.png" mode="aspectFit"></image></view>
		<nav-bar :isback="true" @back="back" leftText="陶老师AI智能刷题" leftType="3" topbgColor="#CAE3FF" :opacity="topOpacity"></nav-bar>
		<view class="page-top" @click="goAnswerSheet" v-if="!sid">
			<image class="img" src="../../static/image/svg/2253.svg" mode="aspectFit"></image>
			<view class="info">
				<view class="t1">每日提升{{$common.dateFormat('','1')}}</view>
				<view class="t2">正确率{{statistics.accuracy}}%&emsp;答对:{{statistics.right}}道&emsp;答错:{{statistics.wrong}}道</view>
			</view>
			<view class="iconfont icon-fanhui base-fanhui"></view>
		</view>
		
		<view class="main-points">
			<view class="title">
				<view class="iconfont icon-shujuditu"></view>
				<view>陶老师AI知识点摘要</view>
			</view>
			<view class="voice" @click="voice">
				<view class="name">语音播报</view>
				<view class="iconfont icon-ting"></view>
			</view>
			<view class="points-html" v-if="aiText1"><text class="ai-text" selectable user-select>{{aiText1}}</text>（AI生成）</view>
			<view class="points-html loading-box" v-else><text>正在生成内容</text>
    <view class="dot"></view>
    <view class="dot"></view>
    <view class="dot"></view></view>
			<view v-if="aiText1" class="points-bottom">
				<view class="left">长按内容即可复制</view>
				<view class="right">
					<view class="item" @click="copyText1('1')">
						<view class="iconfont icon-fuzhi"></view>
						<view class="name">复制</view>
					</view>
					<view v-if="!sid" class="item" @click="commentClick('1')">
						<view class="iconfont icon-xiugaibaobiaomoban"></view>
						<view class="name">笔记</view>
					</view>
				</view>
			</view>
		</view>
		
		<view id="testing" class="testing-centre" v-if="createText2 || !!aiText2">
			<view class="title">
				<view class="iconfont icon-a-xiansuoguanli"></view>
				<view>陶老师考点速记</view>
			</view>
			<view class="voice" @click="voiceBs">
				<view class="name"><view class="iconfont icon-yanjing_xianshi_o"></view>背题</view>
			</view>
			<view v-if="aiText2" class="points-html">
				<text class="ai-text block" v-for="(item,index) in questions" :key="index">
					{{ item.textBefore }} 
					<text class="blank" @click="showAnswer(index)">
					  {{ item.show ? item.answer : '____' }}
					</text>
					<text>{{ item.textAfter }}</text>
				</text>（AI生成）
			</view>
			<view v-else class="points-html loading-box"><text>正在生成内容</text>
				<view class="dot"></view>
				<view class="dot"></view>
				<view class="dot"></view>
			</view>
			<view v-if="aiText2" class="points-bottom">
				<view class="left"></view>
				<view class="right">
					<view class="item" @click="copyText1('2')">
						<view class="iconfont icon-fuzhi"></view>
						<view class="name">复制</view>
					</view>
					<view v-if="!sid" class="item" @click="commentClick('2')">
						<view class="iconfont icon-xiugaibaobiaomoban"></view>
						<view class="name">笔记</view>
					</view>
				</view>
			</view>
		</view>
		
		<view id="xMind" class="x-mind" :class="{'fixed': !createText3 && !aiText3}">
			<view class="title">
				<view class="iconfont icon-siweidaotu"></view>
				<view>陶老师AI思维导图</view>
			</view>
			<view v-if="createText3 && !aiText3" class="points-html loading-box"><text>正在生成内容</text>
    <view class="dot"></view>
    <view class="dot"></view>
    <view class="dot"></view></view>
			<view class="x-mind-img" id="chart">
				<!-- #ifdef MP-WEIXIN -->
			    <canvas id="mindmap" canvas-id="mindmap" type="2d" style="width: 100%; height: 100%; border:1px solid #ccc;"></canvas>
			    <!-- #endif -->
				<!-- #ifdef APP-PLUS -->
				<canvas id="mindmap" canvas-id="mindmap" style="width: 100%; height: 100%; border:1px solid #ccc;"></canvas>
				<!-- #endif -->
				<canvas id="mindmapExport" canvas-id="mindmapExport" style="width:0;height:0;position:absolute;left:-9999rpx;"></canvas>
			</view>
			<view class="check-msg">
				<view>AI生成</view>
				<view @click="magnifyImg">点击查看大图</view>
			</view>
		</view>
		
		<view v-if="false" class="main-points">
			<view class="title">
				<view class="iconfont icon-shujuditu"></view>
				<view>陶老师AI巧记口诀</view>
			</view>
			<view class="voice">
				<view class="name">语音播报</view>
				<view class="iconfont icon-ting"></view>
			</view>
			<view class="points-html">
				密封避光避明火，疫苗冷藏2-10℃。  
				锐器处理五步法，挤冲消报记心间。  
				无菌持物钳闭合，铺盘三边折角严。  
				解析：  
				1. 密封避光避明火：无菌物品需密封保存，避免光照和明火（如乙醇、疫苗需冷藏2-10℃）。  
				2. 锐器处理五步法：锐器伤后按“挤（近心端挤压）、冲（流动水冲洗）、消（碘伏消毒）、报（上报登记）、防（预防用药）”处理。  
				3. 无菌持物钳闭合：取放时钳端闭合，不可触及容器口边缘，使用后及时放回。  
				4. 铺盘三边折角严：无菌盘铺巾时，外层包布向内反折两次，内层向内反折一次，确保无菌面朝上。  
			</view>
			<view class="points-bottom">
				<view class="left"></view>
				<view class="right">
					<view class="item" @click="copyText1">
						<view class="iconfont icon-fuzhi"></view>
						<view class="name">复制</view>
					</view>
					<view class="item" @click="commentClick">
						<view class="iconfont icon-xiugaibaobiaomoban"></view>
						<view class="name">笔记</view>
					</view>
				</view>
			</view>
		</view>
		
		<cover-view class="bottom-btns" v-if="!sid">
			<cover-view class="btn" @click="createAiText('2')">
				<cover-view class="btn-text">考点速记</cover-view>
			</cover-view>
			<!-- <cover-view class="btn">记忆口诀</cover-view> -->
			<cover-view class="btn" @click="createAiText('3')">
				<cover-view class="btn-text">脑图生成</cover-view>
			</cover-view>
		</cover-view>
		
		<servicePopup ref="service2" refel="service2" type="4"></servicePopup>
		<textPopup></textPopup>
		<commentPopup ref="commentPopup"></commentPopup>
	</view>
</template>

<script>
	import {saveAiAnalyze,getAiAnalyzeByPid,getAiAnalyzeById} from '@/api/index.js'
	import * as echarts from 'echarts';
	import servicePopup from "@/components/servicePopup.vue"
	import textPopup from "@/components/textPopup.vue"
	import pageScrollMixin  from "@/mixins/pageScrollMixin.js"
	import commentPopup from "@/components/commentPopup.vue"
	let myChart=null;
	export default {
		mixins: [pageScrollMixin],
		components: {
			servicePopup,
			textPopup,
			commentPopup
		},
		data() {
			return {
				aiText1: '',
				aiText2: '',
				aiText3: '',
				matter: {
					'1': `任务1：结合知识库内的相关知识分析下面刷题数据，生成3个知识点内容，供学员学习。
						请严格按照知识库内的相关知识点内容及内容框架来回答，重点突出【陶老师总结】、【陶老师巧记】。内容从知识点1开始，知识点3结束。
						错题列表：
						导致左心衰竭呼吸困难等症状的原因主要是
						参考答案：D高血压
						我的答案：B.体循环淤血
						心室颤动最常见的病因是
						参考答案：A休克
						我的答案：D.心脏瓣膜病
						患者女，60岁。住院心电图监测时发现患者出现了室性心动过速，心率160次/分，血压120/80mmHg，意识清楚，双肺呼吸音清晰，无湿啰音。
						参考答案：B.阿托品
						我的答案： A.硝酸甘油
						患者男，62岁。突然出现心前区疼痛伴大汗4小时，急诊就医，心电图示:ST段弓背向上抬高。诊断为急性心肌梗死。应用尿激酶治疗的目的是
						参考答案：E.疏通心肌微循环
						我的答案：D.减轻心脏前负荷
						急性心包炎患者1小时前呼吸困难突然加重，不能平卧BP60/40mmHg，颈静脉怒张，心音遥远。最有效的抢救措施是
						参考答案：B.立即行心包穿刺术
						我的答案：C.静脉滴注多巴胺
						患者男，42岁。高血压患者，今日突然出现头痛、呕吐、多汗、面色苍白、视物模糊，测血压250/120mmHg，经及时治疗抢救，血压有所下降，考虑该患者为
						参考答案：A.急进性高血压
						我的答案：E .急性心肌梗死
						心肺复苏时首选的给药途径是
						参考答案：C.骨髓腔注射
						我的答案：E.气管内注射`,
					'2': '利用【任务1】的答案内容生成10道填空题，用于背题，请只输出以下格式：1、胚胎期24周前，主要的造血器官是:（肝）。（除了填空题，不要生成额外的内容）',//考点速记
					'3': '将【任务1】的答案内容生成思维导图，方便用户快速记忆、复习。固定格式（思维导图内容用---包裹，用#数量代表标题层级）',//思维导图
				},
				questions: [],
				statistics: {
					accuracy: 0,
					all: 0,
					right: 0,
					wrong: 0,
				},
				createText2: false,
				createText3: false,
				saveId: '',
				sid: '',
				ctx: null,
			}
		},
		watch: {
			
		},
		computed: {
			SUBJECTBYDEPT() {
				return this.$store.state.SUBJECTBYDEPT;
			},
			DEPTBYKEMULIST() {
				return this.$store.state.DEPTBYKEMULIST;
			},
			loginInfo() {
				return this.$store.state.loginInfo;
			},
			exerciseList() {
				return this.$store.state.exerciseList;
			},
			aiTextAll(){
				return {
					summary: this.aiText1,
					pointShorthand: this.aiText2,
					mindMap: this.aiText3,
				}
			},
		},
		onLoad(options) {
			console.log(uni.createSelectorQuery(),'---228')
			console.log(options,'----options')
			if(options.sid){
				this.sid=options.sid;
				this.getAiAnalyzeById()
			}else{
				this.getStatistics();
			}
		},
		mounted() {
			if (!this.SUBJECTBYDEPT || this.sid) return;
			let list=this.SUBJECTBYDEPT.map((item,index)=>{
				item.index=index;
				return item
			})
			if(this.exerciseList.filter(item=>!!item)?.length<6){
				return uni.showToast({
					title: '答题数不足！',
					icon: 'none'
				})
			}
			list=this.SUBJECTBYDEPT.filter((item,index)=>{
				const exl=this.exerciseList;
				return item.value!=exl[index] && exl[index]
			}).slice(0,10);
			let nlist=this.SUBJECTBYDEPT.filter((item,index)=>{
				const exl=this.exerciseList;
				return item.value==exl[index] && exl[index]
			}).slice(0,10);
			let tlist=[...list];
			if(list.length<6){
				const m=nlist.sort(() => Math.random() - 0.5).slice(0, 6-list.length);
				tlist=[...tlist,...m]
			}
			this.textCreated(tlist)
		},
		methods: {
			back(){
				if(this.sid){
					uni.navigateBack({
					  delta: 1
					})
				}else{
					if(!this.aiText1 || this.createText2 || this.createText3){
						uni.showModal({
							title: '提示',
							content: '将终止AI生成，请确定是否返回',
							success: (res)=>{
								if (res.confirm) {
									uni.navigateBack({
									  delta: 2
									})
								} else if (res.cancel) {
									console.log('用户点击取消');
								}
							}
						})
					}else{
						uni.navigateBack({
						  delta: 1
						})
					}
				}
			},
			getAiAnalyzeById(){
				getAiAnalyzeById({id: this.sid}).then(res=>{
					this.aiText1=res.data?.summary || '';
					this.aiText2=res.data?.pointShorthand || '';
					this.aiText3=res.data?.mindMap || '';
					if(this.aiText2){
						this.parseQuestions()
					}
					if(this.aiText3){
						const data=this.parseMindMapMarkdown(this.aiText3);
						// #ifdef MP-WEIXIN
						this.chartInit(data[0]);
						// #endif
						// #ifndef MP-WEIXIN
						this.chartInit2(data[0]);
						// #endif
					}
				})
			},
			saveAiAnalyze(){
				saveAiAnalyze({...this.aiTextAll,pid: this.SUBJECTBYDEPT?.[0]?.psId}).then(res=>{
					console.log(res,'----239')
					this.saveId=res.data.id;
				})
			},
			// 笔记
			commentClick(t='1'){
				this.$refs.commentPopup.setTextValue(this['aiText'+t],this.saveId);
				this.$refs.commentPopup.openModal('3',true);
			},
			// 复制
			copyText1(t='1'){
				uni.setClipboardData({
					data: this['aiText'+t],
					success: ()=>{
						uni.showToast({
							title: '复制成功',
							icon: 'none'
						})
					}
				});
			},
			// 语音播报
			voice(){
				
			},
			// 生成错题文案
			textCreated(list){
				const exl=this.exerciseList;
				let text=`任务1：结合知识库内的相关知识分析下面刷题数据，生成3个知识点内容，供学员学习。
						请严格按照知识库内的相关知识点内容及内容框架来回答，重点突出【陶老师总结】、【陶老师巧记】。内容从知识点1开始，知识点3结束。
						错题列表：`;
				list.forEach((item,index)=>{
					text+=`(${index+1})：${item.title}
					参考答案：${item.value}${item[item.value.toLowerCase()]}
					我的答案：${exl[item.index]}${item[exl[item.index].toLowerCase()]}`
				})
				this.matter['1']=text;
				this.createAiText('1');
			},
			getStatistics(){
				const exl=this.exerciseList;
				const list=this.SUBJECTBYDEPT;
				const right=list.filter((item,index)=>item.value==exl[index]).length;
				const wrong=list.filter((item,index)=>item.value!=exl[index] && exl[index]).length;
				Object.assign(this.statistics,{
					accuracy: (right+wrong)===0 ? 0 : (right/(right+wrong)*100).toFixed(2),
					all: right+wrong,
					right,
					wrong,
				})
			},
			// 进入答题卡
			goAnswerSheet(){
				uni.redirectTo({
					url: `/pagesC/topicRelated/answerSheet?type=1`
				})
			},
			parseQuestions() {
				  const lines = this.aiText2.split('\n');
				  this.questions = lines.map(line => {
					const match = line.match(/^(.*)（(.*)）(.*)$/);
					if (match) {
					  return {
						textBefore: match[1],
						answer: match[2],
						textAfter: match[3] || '',
						show: true
					  };
					} else {
					  return { textBefore: line, answer: '', textAfter: '', show: true };
					}
				  });
				  console.log(this.questions,'-----200')
			},
			voiceBs(){
				uni.showToast({
					title: '点击填空横线出现答案',
					icon: 'none'
				})
				this.questions=this.questions.map(item=>{
					item.show=false;
					return item
				})
			},
			showAnswer(index) {
				this.questions[index].show = true;
			},
			async createAiText(n) {
				console.log(n,'---316')
				if(['2','3'].includes(n)){
					if(!this.aiText1){
						return uni.showToast({
							title: '需等待AI知识点摘要生成',
							icon: 'none'
						})
					}
					setTimeout(()=>{
						let id=n=='2' ? '#testing' : '#xMind';
						const query = uni.createSelectorQuery().in(this);
						query.select(id).boundingClientRect(rect => {
							if (rect) {
							  uni.pageScrollTo({
								scrollTop: rect.top + (this.scrollTop || 0),
								duration: 300
							  });
							}
						}).exec();9
					},100)
				}
				if(n=='2'){
					this.createText2=true;
				}else if(n=='3'){
					this.createText3=true;
				}
				try {
					const res = await new Promise((resolve, reject) => {
						console.log(327)
						const params={
						  content: this.matter[n],
						  session_id: this.$store.state.uuid, // 建议用 UUID
						  bot_app_key: import.meta.env.VITE_BOT_APP_KEY,
						  visitor_biz_id: this.loginInfo.phone, // 唯一访客 ID
						  stream: "disable" // 非流式
						};
						console.log(params,'-----params')
					  uni.request({
						url: "https://wss.lke.cloud.tencent.com/v1/qbot/chat/sse",
						method: "POST",
						timeout: 240000,
						data: params,
						success: (cres) => resolve(cres.data),
						fail: (err) => reject(err)
					  });
					});
					console.log(res,'----343')
					uni.hideLoading()
				
					// SSE 返回的数据还是 event/data 格式，需要解析
					const lines = res.split("\n");
					let fullText = "";
					let isFirstReplySkipped = false;
					lines.forEach(line => {
					  line = line.trim();
					  if (line.startsWith("data:")) {
						try {
						  const payload = JSON.parse(line.replace(/^data:\s*/, ""));
						  if (payload?.type === "reply") {
							if (!isFirstReplySkipped) {
							  // 跳过第一个 reply
							  isFirstReplySkipped = true;
							  return;
							}
							if (payload.payload?.content) {
							  fullText += payload.payload.content;
							}
						  }
						} catch (e) {
						  console.error("解析失败", e, line);
						}
					  }
					});
					// fullText 就是完整答案，可以直接显示
					this['aiText'+n] = fullText;
					this.saveAiAnalyze()
					if(n=='2'){
						this.createText2=false;
						this.parseQuestions()
					}else if(n=='3'){
						this.createText3=false;
						console.log(fullText,'---253')
						const data=this.parseMindMapMarkdown(fullText);
						console.log(data,'---254')
						// #ifdef MP-WEIXIN
						this.chartInit(data[0]);
						// #endif
						// #ifndef MP-WEIXIN
						this.chartInit2(data[0]);
						// #endif
					}
					console.log(this['aiText'+n], `----this[aiText${n}]`)
				} catch (err) {
					  uni.hideLoading()
					console.error("请求失败:", err);
				}
			},
			parseMindMapMarkdown(mdText) {
			    // 去掉前后的 ---
			    mdText = mdText.trim().replace(/^---\s*\n/, '').replace(/\n---$/, '');
			    
			    const lines = mdText.split('\n').filter(line => line.trim());
			    const root = [];
			    const stack = [];
			
			    lines.forEach(line => {
			        const match = line.match(/^(#{1,6})\s*(.+)$/); // 支持1-6级标题
			        if (match) {
			            const level = match[1].length;
			            const content = match[2].trim();
			            const node = { name: content, children: [] };
			
			            // 弹出比当前层级高或同级的节点
			            while (stack.length && stack[stack.length - 1].level >= level) {
			                stack.pop();
			            }
			
			            if (stack.length === 0) {
			                root.push(node);
			            } else {
			                stack[stack.length - 1].node.children.push(node);
			            }
			
			            stack.push({ level, node });
			        }
			    });
			    return root;
			},
			magnifyImg(){
				// #ifdef MP-WEIXIN
				this.saveAsImage()
				// #endif
				// #ifndef MP-WEIXIN
				this.saveAsImage2()
				// #endif
			},
			measure(node, baseNodeWidth = 120, nodeHeight = 40, hGap = 40, vGap = 20, ctx) {
			  // 根据文字动态宽度
			  let textWidth = ctx ? ctx.measureText(node.name).width + 20 : baseNodeWidth; // 加内边距
			  let nodeWidth = Math.max(baseNodeWidth, textWidth); // 至少 120
			
			  if (!node.children || node.children.length === 0) {
			    return { w: nodeWidth, h: nodeHeight, nodeWidth };
			  }
			
			  let totalH = 0;
			  let maxW = 0;
			  node.children.forEach(child => {
			    const size = this.measure(child, baseNodeWidth, nodeHeight, hGap, vGap, ctx);
			    totalH += size.h + vGap;
			    maxW = Math.max(maxW, size.w + hGap);
			  });
			  totalH -= vGap;
			  return { w: nodeWidth + maxW, h: Math.max(totalH, nodeHeight), nodeWidth };
			},
			measure2(node, baseNodeWidth = 120, nodeHeight = 40, hGap = 40, vGap = 20, ctx) {
			  // 根据文字动态宽度
			  let textWidth;
			  if (ctx && ctx.measureText) {
			    textWidth = ctx.measureText(node.name).width + 20;
			  } else {
			    // 备用方案：估算文字宽度
			    textWidth = node.name.length * 12 + 20;
			  }
			  
			  let nodeWidth = Math.max(baseNodeWidth, textWidth);
			
			  if (!node.children || node.children.length === 0) {
			    return { w: nodeWidth, h: nodeHeight, nodeWidth };
			  }
			
			  let totalH = 0;
			  let maxW = 0;
			  node.children.forEach(child => {
			    const size = this.measure2(child, baseNodeWidth, nodeHeight, hGap, vGap, ctx);
			    totalH += size.h + vGap;
			    maxW = Math.max(maxW, size.w + hGap);
			  });
			  totalH -= vGap;
			  return { w: nodeWidth + maxW, h: Math.max(totalH, nodeHeight), nodeWidth };
			},
			saveAsImage() {
				if (!this.aiText3) return;
				const data=this.parseMindMapMarkdown(this.aiText3);
				const root=data[0];
				console.log(root,'---root')
				let nodeWidth = 180, nodeHeight = 40, hGap = 40, vGap = 20;
				let textWidth = 40 + root?.name?.length * 11; // 加内边距
				// 13 180
				nodeWidth = Math.max(120, textWidth); // 至少 120
				  const treeSize = this.measure(root, nodeWidth, nodeHeight, hGap, vGap);
			
				  const highResWidth = treeSize.w + 200;
				  const highResHeight = treeSize.h + 200;
				  let offscreen=null;
				  offscreen = wx.createOffscreenCanvas({
					type: '2d',
					width: highResWidth,
					height: highResHeight
				  });
				  let ctx = offscreen.getContext('2d');
			
				  // 白底（可选）
				  ctx.fillStyle = "#fff";
				  ctx.fillRect(0, 0, highResWidth, highResHeight);
			
				  // 居中
				  this.drawNode(ctx, root, 100, treeSize.h/2 + 100, nodeWidth, nodeHeight, hGap, vGap);
				  
				  // 🔑 添加水印
				  ctx.save();
				  ctx.font = "bold 36px sans-serif";
				  ctx.fillStyle = "rgba(0, 0, 0, 0.15)"; // 半透明黑
				  ctx.textAlign = "right";
				  ctx.textBaseline = "bottom";
				  ctx.fillText("陶老师护考", highResWidth - 40, highResHeight - 40);
				  ctx.restore();
			
				  wx.canvasToTempFilePath({
					canvas: offscreen,
					width: highResWidth,
					height: highResHeight,
					destWidth: highResWidth,
					destHeight: highResHeight,
					success: res => {
					  wx.previewImage({ urls: [res.tempFilePath] });
					},
					fail: err => {
					  console.error("生成高清图失败", err);
					}
				  });
			},
			saveAsImage2() {
			  if (!this.aiText3) return;
			  const data = this.parseMindMapMarkdown(this.aiText3);
			  const root = data[0];
			  
			  let nodeWidth = 180, nodeHeight = 40, hGap = 40, vGap = 20;
			  let textWidth = 40 + root?.name?.length * 11;
			  nodeWidth = Math.max(120, textWidth);
			  
			  const treeSize = this.measure2(root, nodeWidth, nodeHeight, hGap, vGap);
			  const highResWidth = treeSize.w + 200;
			  const highResHeight = treeSize.h + 200;
			  
			  let offscreen = null;
			  let ctx = null;
			  
			  // 平台差异处理
			  // #ifdef APP-PLUS
			  offscreen = plus.nativeObj.View.getOffscreenCanvas('mindmap');
			  ctx = offscreen.getContext('2d');
			  offscreen.width = highResWidth;
			  offscreen.height = highResHeight;
			  // #endif
			  
			  // #ifdef MP-WEIXIN
			  offscreen = wx.createOffscreenCanvas({
			    type: '2d',
			    width: highResWidth,
			    height: highResHeight
			  });
			  ctx = offscreen.getContext('2d');
			  // #endif
			  
			  if (!ctx) {
			    console.error('无法创建 Canvas 上下文');
			    return;
			  }
			
			  // 白底
			  ctx.fillStyle = "#fff";
			  ctx.fillRect(0, 0, highResWidth, highResHeight);
			
			  // 绘制节点
			  this.drawNode(ctx, root, 100, treeSize.h/2 + 100, nodeWidth, nodeHeight, hGap, vGap);
			  
			  // 水印
			  ctx.save();
			  ctx.font = "bold 36px sans-serif";
			  ctx.fillStyle = "rgba(0, 0, 0, 0.15)";
			  ctx.textAlign = "right";
			  ctx.textBaseline = "bottom";
			  ctx.fillText("陶老师护考", highResWidth - 40, highResHeight - 40);
			  ctx.restore();
			
			  // 保存图片
			  this.saveCanvasToImage(offscreen, highResWidth, highResHeight);
			},
			// 统一的保存方法
			saveCanvasToImage(canvas, width, height) {
			  // #ifdef MP-WEIXIN
			  wx.canvasToTempFilePath({
			    canvas: canvas,
			    width: width,
			    height: height,
			    destWidth: width,
			    destHeight: height,
			    success: res => {
			      wx.previewImage({ urls: [res.tempFilePath] });
			    },
			    fail: err => {
			      console.error("生成高清图失败", err);
			    }
			  });
			  // #endif
			  
			  // #ifdef APP-PLUS
			  canvas.toTempFilePath({
			    x: 0,
			    y: 0,
			    width: width,
			    height: height,
			    destWidth: width,
			    destHeight: height,
			    fileType: 'png',
			    quality: 1,
			    success: (res) => {
			      // App 端预览图片
			      plus.nativeUI.previewImage([res.tempFilePath]);
			    },
			    fail: (err) => {
			      console.error('App 端生成图片失败', err);
			    }
			  });
			  // #endif
			},
			// 绘制节点（只画框和连线，不加额外背景）
			drawNode(ctx, node, x, y, baseNodeWidth, nodeHeight, hGap, vGap) {
			  // 动态宽度
			  const textWidth = ctx.measureText(node.name).width + 20;
			  const nodeWidth = Math.max(baseNodeWidth, textWidth);
			
			  // 绘制框
			  ctx.fillStyle = "#1976d2";
			  ctx.fillRect(x, y - nodeHeight/2, nodeWidth, nodeHeight);
			
			  // 绘制文字
			  ctx.fillStyle = "#fff";
			  ctx.textAlign = "center";
			  ctx.textBaseline = "middle";
			  ctx.font = "12px sans-serif";
			  ctx.fillText(node.name, x + nodeWidth/2, y);
			
			  // 子节点递归
			  if (node.children && node.children.length > 0) {
			    let totalH = 0;
				// #ifdef MP-WEIXIN
			    node.children.forEach(c => totalH += this.measure(c, baseNodeWidth, nodeHeight, hGap, vGap, ctx).h + vGap);
				// #endif
				// #ifndef MP-WEIXIN
				node.children.forEach(c => totalH += this.measure2(c, baseNodeWidth, nodeHeight, hGap, vGap, ctx).h + vGap);
				// #endif
			    totalH -= vGap;
			    let cy = y - totalH / 2;
			
			    node.children.forEach(child => {
				  // #ifdef MP-WEIXIN
			      const size = this.measure(child, baseNodeWidth, nodeHeight, hGap, vGap, ctx);
				  // #endif
				  // #ifndef MP-WEIXIN
				  const size = this.measure2(child, baseNodeWidth, nodeHeight, hGap, vGap, ctx);
				  // #endif
			      const childX = x + nodeWidth + hGap;
			      const childY = cy + size.h / 2;
			
			      // 连线
			      ctx.strokeStyle = "#666";
			      ctx.beginPath();
			      ctx.moveTo(x + nodeWidth, y);
			      ctx.lineTo(childX, childY);
			      ctx.stroke();
			
			      this.drawNode(ctx, child, childX, childY, baseNodeWidth, nodeHeight, hGap, vGap);
			      cy += size.h + vGap;
			    });
			  }
			},
			/** 初始化缩略图 */
			chartInit(data) {
			  const query = uni.createSelectorQuery().in(this);
			  query.select('#mindmap')
			    .fields({ node: true, size: true })
			    .exec(res => {
			      if (!res[0] || !res[0].node) {
			        console.error("canvas 节点没找到");
			        return;
			      }
				  
				 let canvas = res[0].node;
				 let ctx = canvas.getContext('2d');
			
			      // 确保高分辨率
				  let dpr=null;
			      dpr = uni.getSystemInfoSync().pixelRatio;
			      canvas.width = res[0].width * dpr;
			      canvas.height = res[0].height * dpr;
			      ctx.scale(dpr, dpr);
			
			      // 清空画布
			      ctx.clearRect(0, 0, res[0].width, res[0].height);
			
			      // 计算树的尺寸（注意参数顺序！）
			      const treeSize = this.measure(data, 120, 40, 40, 20, ctx);
			
			      // 缩放以适配缩略图
			      const scale = Math.min(
			        res[0].width / (treeSize.w + 50),
			        res[0].height / (treeSize.h + 50)
			      );
			
			      ctx.save();
			      ctx.scale(scale, scale);
			
			      // 平移居中
			      const offsetX = (res[0].width / scale - treeSize.w) / 2;
			      const offsetY = (res[0].height / scale - treeSize.h) / 2;
			      ctx.translate(offsetX, offsetY);
			
			      // 绘制
			      this.drawNode(ctx, data, 0, treeSize.h / 2, 120, 40, 40, 20);
			
			      ctx.restore();
			    });
			},
			chartInit2(data) {
			  const query = uni.createSelectorQuery().in(this);
			  query.select('#mindmap')
			    .fields({ node: true, size: true })
			    .exec(res => {
			      console.log('Canvas查询结果:', res); // 添加调试日志
			      
			      if (!res[0] || !res[0].node) {
			        console.error("canvas 节点没找到", res);
			        // 备用方案：使用 canvas-id 方式
			        this.chartInit2Fallback(data);
			        return;
			      }
			      
			      let canvas = res[0].node;
			      let ctx = canvas.getContext('2d');
			      
			      if (!ctx) {
			        console.error("无法获取 Canvas 上下文");
			        return;
			      }
			      
			      console.log('Canvas 尺寸:', res[0].width, res[0].height);
			      
			      // 获取设备像素比
			      let dpr = 1;
			      // #ifdef MP-WEIXIN
			      dpr = uni.getSystemInfoSync().pixelRatio;
			      // #endif
			      // #ifdef APP-PLUS
			      dpr = plus.screen.scale;
			      // #endif
			      
			      canvas.width = res[0].width * dpr;
			      canvas.height = res[0].height * dpr;
			      ctx.scale(dpr, dpr);
			
			      // 清空画布
			      ctx.clearRect(0, 0, res[0].width, res[0].height);
			
			      // 计算树的尺寸
			      const treeSize = this.measure2(data, 120, 40, 40, 20, ctx);
			      console.log('思维导图尺寸:', treeSize);
			
			      // 缩放以适配缩略图
			      const scale = Math.min(
			        res[0].width / (treeSize.w + 50),
			        res[0].height / (treeSize.h + 50)
			      );
			      console.log('缩放比例:', scale);
			
			      ctx.save();
			      ctx.scale(scale, scale);
			
			      // 平移居中
			      const offsetX = (res[0].width / scale - treeSize.w) / 2;
			      const offsetY = (res[0].height / scale - treeSize.h) / 2;
			      ctx.translate(offsetX, offsetY);
			
			      // 绘制
			      this.drawNode(ctx, data, 0, treeSize.h / 2, 120, 40, 40, 20);
			
			      ctx.restore();
			    });
			},
			// 备用初始化方案
			chartInit2Fallback(data) {
			  // #ifdef APP-PLUS
			  const ctx = uni.createCanvasContext('mindmap', this);
			  
			  // 获取 Canvas 尺寸
			  const query = uni.createSelectorQuery().in(this);
			  query.select('#mindmap').boundingClientRect(rect => {
			    if (!rect) {
			      console.error("无法获取 Canvas 尺寸");
			      return;
			    }
			    
			    const width = rect.width;
			    const height = rect.height;
			    
			    // 清空画布
			    ctx.clearRect(0, 0, width, height);
			    
			    // 计算树的尺寸（需要修改 measure 方法支持无 ctx 的情况）
			    const treeSize = this.measure2(data, 120, 40, 40, 20, null);
			    
			    // 缩放以适配
			    const scale = Math.min(
			      width / (treeSize.w + 50),
			      height / (treeSize.h + 50)
			    );
			    
			    ctx.scale(scale, scale);
			    
			    // 平移居中
			    const offsetX = (width / scale - treeSize.w) / 2;
			    const offsetY = (height / scale - treeSize.h) / 2;
			    ctx.translate(offsetX, offsetY);
			    
			    // 使用兼容的绘制方法
			    this.drawNodeCompat(ctx, data, 0, treeSize.h / 2, 120, 40, 40, 20);
			    
			    ctx.draw();
			  }).exec();
			  // #endif
			},
			// 兼容的绘制方法（使用 uni CanvasContext）
			drawNodeCompat(ctx, node, x, y, baseNodeWidth, nodeHeight, hGap, vGap) {
			  // 估算文字宽度（App端可能不支持 measureText）
			  const textWidth = node.name.length * 12 + 20;
			  const nodeWidth = Math.max(baseNodeWidth, textWidth);
			  
			  // 绘制框
			  ctx.setFillStyle("#1976d2");
			  ctx.fillRect(x, y - nodeHeight/2, nodeWidth, nodeHeight);
			  
			  // 绘制文字
			  ctx.setFillStyle("#fff");
			  ctx.setTextAlign("center");
			  ctx.setTextBaseline("middle");
			  ctx.setFontSize(12);
			  ctx.fillText(node.name, x + nodeWidth/2, y);
			  
			  // 子节点递归
			  if (node.children && node.children.length > 0) {
			    let totalH = 0;
			    node.children.forEach(c => {
			      const size = this.measure2(c, baseNodeWidth, nodeHeight, hGap, vGap, null);
			      totalH += size.h + vGap;
			    });
			    totalH -= vGap;
			    let cy = y - totalH / 2;
			    
			    node.children.forEach(child => {
			      const size = this.measure2(child, baseNodeWidth, nodeHeight, hGap, vGap, null);
			      const childX = x + nodeWidth + hGap;
			      const childY = cy + size.h / 2;
			      
			      // 连线
			      ctx.setStrokeStyle("#666");
			      ctx.setLineWidth(1);
			      ctx.beginPath();
			      ctx.moveTo(x + nodeWidth, y);
			      ctx.lineTo(childX, childY);
			      ctx.stroke();
			      
			      this.drawNodeCompat(ctx, child, childX, childY, baseNodeWidth, nodeHeight, hGap, vGap);
			      cy += size.h + vGap;
			    });
			  }
			},
		}
	}
</script>

<style lang="scss" scoped>
	.content{
		min-height: 100vh;
		padding: 12rpx 28rpx 140rpx;
		background: linear-gradient( 180deg, #CAE3FF 0%, #FFFFFF 34%, #EEF1F2 66%, #EEF1F2 100%);
		overflow: hidden;
	}
	.page-top,.main-points,.testing-centre,.x-mind{
		margin-bottom: 28rpx;
		border-radius: 26rpx;
		background-color: #fff;
	}
	.points-html{
		min-height: 200rpx;
	}
	.page-top{
		display: flex;
		align-items: center;
		padding: 22rpx 35rpx 26rpx 26rpx;
		.img{
			width: 67rpx;
			height: 71.5rpx;
			margin-right: 18rpx;
		}
		.info{
			flex: 1;
			.t1{
				margin-bottom: 6rpx;
				font-size: 26rpx;
				font-weight: 600;
				color: #323232;
			}
			.t2{
				font-size: 24rpx;
				color: #9A9A9A;
			}
		}
		.iconfont{
			font-size: 30rpx;
			color: #747474;
		}
	}
	.main-points,.testing-centre,.x-mind{
		position: relative;
		margin-bottom: 32rpx;
		border: 2rpx solid #E9E9E9;
	}
	.main-points,.testing-centre,.x-mind{
		padding: 56rpx 36rpx 38rpx;
		.title{
			display: flex;
			align-items: center;
			margin-bottom: 34rpx;
			font-size: 26rpx;
			font-weight: 600;
			color: #202020;
			.iconfont{
				margin-right: 8rpx;
				font-weight: 500;
				color: #4779DE;
			}
		}
		.voice{
			position: absolute;
			top: 36rpx;
			right: 38rpx;
			display: flex;
			align-items: center;
			cursor: pointer;
			.name{
				display: flex;
				justify-content: center;
				align-items: center;
				width: 96rpx;
				height: 34rpx;
				margin-right: 8rpx;
				font-size: 16rpx;
				color: #3E67BB;
				border: 2rpx solid #3E67BB;
				border-radius: 10rpx;
			}
			.iconfont{
				font-size: 52rpx;
				color: #3E67BB;
			}
		}
		.points-html{
			margin-bottom: 30rpx;
			font-size: 24rpx;
			line-height: 48rpx;
		}
		.points-bottom{
			display: flex;
			justify-content: space-between;
			align-items: center;
			.left{
				font-size: 20rpx;
				color: #BFBFBF;
			}
			.right{
				display: flex;
				text-align: center;
			}
			.iconfont{
				margin-bottom: 2rpx;
				font-size: 30rpx;
			}
			.right{
				color: #515151;
				font-size: 10rpx;
			}
			.item{
				position: relative;
				margin-left: 60rpx;
				&:nth-child(2){
					&::after{
						content: "";
						position: absolute;
						top: 50%;
						left: -30rpx;
						width: 2rpx;
						height: 30rpx;
						margin-top: -15rpx;
						background-color: #707070;
						opacity: .15;
					}
				}
			}
		}
	}
	.testing-centre{
		padding: 40rpx 36rpx 38rpx;
		.voice{
			top: 40rpx;
			right: 44rpx;
			.name{
				width: 111rpx;
				height: 41rpx;
				font-size: 16rpx;
				.iconfont{
					margin-right: 5rpx;
					font-size: 30rpx;
				}
			}
		}
	}
	.x-mind{
		padding-bottom: 28rpx;
		&.fixed{
			position: fixed;
			top: -10000rpx;
			left: -1000rpx;
			width: 624rpx
		}
		.title{
			margin-bottom: 32rpx;
		}
		.x-mind-img{
			height: 236rpx;
			overflow: hidden;
			margin-bottom: 10rpx;
			border-radius: 20rpx;
			border: 2rpx solid #E9E9E9;
		}
		.check-msg{
			display: flex;
			justify-content: space-between;
			font-size: 20rpx;
			color: #BFBFBF;
		}
	}
	.bottom-btns{
		position: fixed;
		bottom: 0;
		left: 0;
		right: 0;
		z-index: 1;
		display: flex;
		justify-content: flex-end;
		padding: 28rpx 28rpx 92rpx 0;
		color: #202020;
		background-color: #eef1f2;
		.btn{
			display: flex;
			justify-content: center;
			align-items: center;
			width: 132rpx;
			height: 56rpx;
			margin-left: 28rpx;
			line-height: 56rpx;
			text-align: center;
			border-radius: 14rpx;
			background-color: #fff;
			font-size: 22rpx;
			line-height: 1;
			transform: translateY(-2rpx);
		}
		.btn-text {
		    display: flex;
		    align-items: center;
		    justify-content: center;
		}
	}
	.ai-text{
		white-space: pre-wrap; /* 保留换行和空格 */
		word-break: break-word; /* 超长文本换行 */
		&.block{
			display: block;
		}
		.blank{
			cursor: pointer;
			color: #4779DE;
		}
	}
	.css-box {
	  padding: 6px 8px;
	}
	.loading-box {
	  display: flex;
	  align-items: center;
	  font-size: 14px;
	}
	
	.dot {
	  display: inline-block;
	  width: 6px;
	  height: 6px;
	  margin-left: 2px;
	  border-radius: 50%;
	  background: #333;
	  opacity: 0;
	  animation: blink 1.5s infinite;
	}
	
	/* 依次延迟 */
	.dot:nth-child(2) { animation-delay: 0.3s; }
	.dot:nth-child(3) { animation-delay: 0.6s; }
	
	@keyframes blink {
	  0%, 80%, 100% { opacity: 0; }
	  40% { opacity: 1; }
	}
</style>
